
#include <cstdio>
#include <cstring>
#include <cerrno>

#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#include "libnet_error.h"

#include "net_tcp_server.h"



CNetTCPServer::CNetTCPServer(INetEventHandler& refEventHandler, INetTCPConnectionCallback& refCallback, const sockaddr_in& refAddr):
    m_refEventHandler(refEventHandler),
    m_refCallback(refCallback),
    m_stAddr(refAddr)
{
    FD_ZERO(&m_stOriginReadSet);
    FD_ZERO(&m_stOriginWriteSet);
}

CNetTCPServer::~CNetTCPServer()
{
    
}

int32_t CNetTCPServer::Start()
{
    pobjListener = CNetConnectionHelper::CreateListener(&m_refEventHandler, this, &m_stAddr);
    if (pobjListener == nullptr)
    {
        return ERROR_FAILED;
    }

    m_bRunning = true;
    m_objThread = std::thread(&CNetTCPServer::__ThreadFunc, this);

    return ERROR_SUCCESS;
}

void CNetTCPServer::Stop()
{

}

void CNetTCPServer::OnConnected(INetTCPConnection *pobjConn)
{
    m_refCallback.OnConnected(pobjConn);
}

void CNetTCPServer::OnDisconnected(INetTCPConnection *pobjConn) 
{
    m_refCallback.OnDisconnected(pobjConn);

    if (pobjListener != nullptr)
    {
        pobjListener->DeleteConnection(pobjConn);
    }
}

void CNetTCPServer::OnMessage(const void* pData, size_t ulDataLen) 
{
    m_refCallback.OnMessage(pData, ulDataLen);
}


int32_t CNetTCPServer::__CreateListener()
{
    int iSockFd = socket(AF_INET, SOCK_STREAM|SOCK_NONBLOCK, IPPROTO_TCP);
    if (iSockFd == -1)
    {
        printf("socket error, %s\n", strerror(errno));
        return -1;
    }

    int iErr = bind(iSockFd, (const struct sockaddr*)&m_stAddr, sizeof(m_stAddr));
    if (iErr == -1)
    {
        close(iSockFd);
        return -1;
    }

    iErr = listen(iSockFd, 1024);
    if (iErr == -1)
    {
        close(iSockFd);
        return -1;
    }

    __AddREvent(iSockFd);

    return iSockFd;
}

ssize_t CNetTCPServer::__RecvOne(int iFd, void* pBuf, size_t ulBufLen)
{
     ssize_t lRecv =  0;
    bool bRetry = false;

    do
    {
        lRecv = recv(iFd, pBuf, ulBufLen, 0);
        if (lRecv == -1)
        {
            if (errno == EINTR)
            {
                // 被信号打断，需要重试
                bRetry = true;
                continue;
            }

            if (errno == EAGAIN || errno == EWOULDBLOCK)
            {
                // 非阻塞模式，没有数据可以读
                lRecv = 0;
                break;
            }
        }
        else if (lRecv == 0)
        {
            // 对端执行了正常的连接关闭
            lRecv = -1;
        }
    } while (bRetry);

    return lRecv;
}

void CNetTCPServer::__Recv(int iFd)
{
    char acBuf[4096];
    ssize_t lRecvLen = 0;

    do
    {
        lRecvLen = __RecvOne(iFd, acBuf, sizeof(acBuf));
        if (lRecvLen == -1)
        {
            printf("Connection error\n");
            __DeleteREvent(iFd);
            __DeleteWEvent(iFd);
            close(iFd);
        }
        else if (lRecvLen > 0)
        {
            printf("Received message: %s\n", acBuf);
        }
    } while (lRecvLen == sizeof(acBuf));
}

void CNetTCPServer::__AddREvent(int iFd)
{
    printf("Add readable event, %d\n", iFd);
    FD_SET(iFd, &m_stOriginReadSet);

    if (iFd > m_iMaxFd)
    {
        m_iMaxFd = iFd;
    }
}

void CNetTCPServer::__DeleteREvent(int iFd)
{
    printf("Delete readable event, %d\n", iFd);
    FD_CLR(iFd, &m_stOriginReadSet);
}

void CNetTCPServer::__DealREvent(int iFd)
{
    printf("Deal readable event, %d\n", iFd);

    if (iFd == m_iListenSockFd)
    {
        sockaddr_in stAddr;
        socklen_t uiAddrLen = sizeof(stAddr);

        int iAcceptFd = accept(iFd, (sockaddr*)&stAddr, &uiAddrLen);
        if (iAcceptFd == -1)
        {
            perror("Failed to accept");
        }
        else
        {
            __AddREvent(iAcceptFd);
        }
    }
    else
    {
        __Recv(iFd);
    }
}

void CNetTCPServer::__AddWEvent(int iFd)
{
    printf("Add writeable event, %d\n", iFd);
    FD_SET(iFd, &m_stOriginWriteSet);

    if (iFd > m_iMaxFd)
    {
        m_iMaxFd = iFd;
    }
}

void CNetTCPServer::__DeleteWEvent(int iFd)
{
    printf("Delete writeable event, %d\n", iFd);
    FD_CLR(iFd, &m_stOriginWriteSet);
}

void CNetTCPServer::__DealWEvent(int iFd)
{
    printf("Deal writeable event, %d\n", iFd);
}

int32_t CNetTCPServer::__WaitEvent()
{
    m_stTmpReadSet = m_stOriginReadSet;
    m_stTmpWriteSet = m_stOriginWriteSet;

    int iFdNum = m_iMaxFd + 1;
    int iRes = select(iFdNum, &m_stTmpReadSet, &m_stTmpWriteSet, nullptr, nullptr);
    if (iRes <= 0 && errno != EINTR)
    {
        return ERROR_FAILED;
    }

    printf("Wait result: %d\n", iRes);
    for (int i = 0; iRes > 0 && i < iFdNum; i++)
    {
        bool bHappen = false;

        if (FD_ISSET(i, &m_stTmpReadSet))
        {
            bHappen = true;

            __DealREvent(i);
        }

        if (FD_ISSET(i, &m_stTmpWriteSet))
        {
            bHappen = true;
            __DealWEvent(i);
        }

        if (bHappen)
        {
            iRes--;
        }
    }
}

void CNetTCPServer::__ThreadFunc()
{
    printf("Thread is running\n");

    int32_t iErr;

    while (m_bRunning)
    {
        iErr = m_refEventHandler.Wait();

        if (iErr == ERROR_FAILED)
        {
            perror("Wait event error");
            break;
        }
    }

    printf("Thread will exit\n");
}


INetTCPServer* NewTCPServer(INetEventHandler* pobjEventHandler, INetTCPConnectionCallback* pobjCallback, const char* pcHost, uint16_t usPort)
{
    if (pobjEventHandler == nullptr || pcHost == nullptr || *pcHost == '\0' || usPort == 0)
    {
        printf("Invalid parameter\n");
        return nullptr;
    }

    sockaddr_in stAddr{};

    if (inet_pton(AF_INET, pcHost, &stAddr.sin_addr) != 1)
    {
        printf("Invalid parameter\n");
        return nullptr;
    }

    stAddr.sin_family = AF_INET;
    stAddr.sin_port = htons(usPort);

    return new (std::nothrow) CNetTCPServer(*pobjEventHandler, *pobjCallback, stAddr);
}

void DeleteTCPServer(INetTCPServer* pobjServer)
{

}